﻿using System;
using System.Diagnostics;
using System.Text;

namespace FilterIllegalCharacter.Core
{
    /// <summary>
    /// 敏感词过滤器
    /// </summary>
    public class FilterHelper
    {
        /// <summary>
        /// 进度条事件
        /// </summary>
        public EventHandler<double>? ProgressBarEvent;
        /// <summary>
        /// 单例对象
        /// </summary>
        private static FilterHelper? _instance;
        /// <summary>
        /// 过滤器实例
        /// </summary>
        public static FilterHelper Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new FilterHelper();
                }
                return _instance;
            }
        }

        private WordNode? wordNodes;

        public void ReInitDictionary(string[] words)
        {
            ClearDictionary();
            InitDictionary(words);
        }
        /// <summary>
        /// 初始化敏感词字典
        /// </summary>
        /// <param name="words"></param>
        public void InitDictionary(string[] words)
        {
            if (wordNodes == null)
            {
                wordNodes = new WordNode();
            }
            for (int i = 0, Imax = words.Length; i < Imax; i++)
            {
                string word = words[i];
                string dbcWord = ToDBC(word);
                for (int j = dbcWord.Length - 1; j >= 0; j--)
                {
                    // 特殊字符过滤,如果不是通配符且是特殊字符则过滤
                    if (dbcWord[j] != '*' && CheckSpecial.CheckSpecialSym(dbcWord[j]))
                    {
                        dbcWord = dbcWord.Remove(j, 1);
                    }
                }
                if (dbcWord.Length > 0)
                {
                    wordNodes.Add(dbcWord);
                }
            }
        }
        public void ClearDictionary()
        {
            wordNodes = null;
        }

        public bool CheckBadWord(string sourceText, int filterNum = 0)
        {
            if (sourceText != string.Empty)
            {
                string sourceDBCText = ToDBC(sourceText);
                for (int i = 0; i < sourceDBCText.Length; i++)
                {
                    int badWordLen = 0;
                    if (filterNum > 0 && CheckSpecial.IsNum(sourceDBCText[i]))
                    {
                        badWordLen = CheckNumberSeq(sourceDBCText, i, filterNum);
                        if (badWordLen > 0)
                        {
                            return true;
                        }
                    }
                    //查询以该字为首字符的词组  
                    badWordLen = Check(sourceDBCText, i);
                    if (badWordLen > 0)
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 获取关键字索引长度
        /// </summary>
        /// <param name="token">任务取消令牌</param>
        /// <param name="sourceText"></param>
        /// <param name="filterNum"></param>
        /// <returns></returns>
        public List<RNode> GetRNodeList(CancellationToken token, string sourceText, int filterNum = 0)
        {
            ProgressBarEvent?.Invoke(this, 0d);
            if (!string.IsNullOrEmpty(sourceText))
            {
                string sourceDBCText = ToDBC(sourceText);

                char[] tempString = sourceText.ToCharArray();

                List<RNode> replaceList = new List<RNode>();
                for (int i = 0; i < sourceDBCText.Length; i++)
                {
                    if (token.IsCancellationRequested)
                    {
                        token.ThrowIfCancellationRequested();
                    }
                    int badWordLen = 0;
                    if (filterNum > 0 && CheckSpecial.IsNum(sourceDBCText[i]))
                    {
                        badWordLen = CheckNumberSeq(sourceDBCText, i, filterNum);
                        if (badWordLen > 0)
                        {
                            badWordLen = badWordLen + 1;
                            StringBuilder sb = new StringBuilder();
                            for (int pos = 0; pos < badWordLen; pos++)
                            {
                                sb.Append(tempString[pos + i]);
                            }
                            replaceList.Add(new RNode { start = i, len = badWordLen, type = 1, text = sb.ToString() });
                            i = i + badWordLen - 1;
                            continue;
                        }
                    }

                    //查询以该字为首字符的词组  
                    badWordLen = Check(sourceDBCText, i);
                    if (badWordLen > 0)
                    {
                        StringBuilder sb = new StringBuilder();
                        for (int pos = 0; pos < badWordLen; pos++)
                        {
                            sb.Append(tempString[pos + i]);
                        }
                        replaceList.Add(new RNode { start = i, len = badWordLen, type = 0, text = sb.ToString() });
                        i = i + badWordLen - 1;
                    }
                    double progress = i * 100d / sourceDBCText.Length;
                    ProgressBarEvent?.Invoke(this, progress);
                }
                ProgressBarEvent?.Invoke(this, 100);
                return replaceList;
            }
            else
            {
                throw new Exception("请输入待过滤字符串");
            }
        }

        public string FilterWithChar(string sourceText, char replaceChar, int filterNum = 0, string numReplace = null)
        {
            //var timeStart = System.DateTime.Now;
            if (sourceText != string.Empty)
            {
                string sourceDBCText = ToDBC(sourceText);

                char[] tempString = sourceText.ToCharArray();
                List<RNode> replaceList = new List<RNode>();

                for (int i = 0; i < sourceDBCText.Length; i++)
                {
                    int badWordLen = 0;
                    if (filterNum > 0 && CheckSpecial.IsNum(sourceDBCText[i]))
                    {
                        badWordLen = CheckNumberSeq(sourceDBCText, i, filterNum);
                        if (badWordLen > 0)
                        {
                            badWordLen = badWordLen + 1;
                            if (numReplace == null)
                            {
                                for (int pos = 0; pos < badWordLen; pos++)
                                {
                                    tempString[pos + i] = replaceChar;
                                }
                            }
                            else
                            {
                                replaceList.Add(new RNode { start = i, len = badWordLen, type = 1 });
                            }
                            i = i + badWordLen - 1;
                            continue;
                        }
                    }

                    //查询以该字为首字符的词组  
                    badWordLen = Check(sourceDBCText, i);
                    if (badWordLen > 0)
                    {
                        for (int pos = 0; pos < badWordLen; pos++)
                        {
                            tempString[pos + i] = replaceChar;
                        }
                        i = i + badWordLen - 1;
                    }
                }
                string result;
                if (replaceList.Count > 0)
                {
                    result = replaceString(tempString, replaceList, null, numReplace);
                }
                else
                {
                    result = new string(tempString);
                }
                //double timeCost = (System.DateTime.Now - timeStart).TotalMilliseconds;
                //Debug.LogError("timeCost:" + timeCost + " ms");
                return result;
            }
            else
            {
                return string.Empty;
            }
        }

        public string FilterWithStr(string sourceText, string replaceStr, int filterNum = 0, string? numReplace = null)
        {
            //var timeStart = System.DateTime.Now;
            if (sourceText != string.Empty)
            {
                string sourceDBCText = ToDBC(sourceText);
                List<RNode> replaceList = new List<RNode>();

                if (filterNum > 0 && numReplace == null)
                {
                    numReplace = replaceStr;
                }
                int badWordLen = 0;
                for (int i = 0; i < sourceDBCText.Length; i++)
                {
                    if (filterNum > 0 && CheckSpecial.IsNum(sourceDBCText[i]))
                    {
                        badWordLen = CheckNumberSeq(sourceDBCText, i, filterNum);
                        if (badWordLen > 0)
                        {
                            badWordLen = badWordLen + 1;
                            int start = i;
                            replaceList.Add(new RNode { start = start, len = badWordLen, type = 1 });
                            i = i + badWordLen - 1;
                            continue;
                        }
                    }

                    badWordLen = Check(sourceDBCText, i);
                    if (badWordLen > 0)
                    {
                        replaceList.Add(new RNode { start = i, len = badWordLen, type = 0 });
                        i = i + badWordLen - 1;
                    }
                }
                string tempStr = replaceString(sourceText.ToCharArray(), replaceList, replaceStr, numReplace);
                return tempStr;
            }
            else
            {
                return string.Empty;
            }
        }

        private string replaceString(char[] charArry, List<RNode> nodes, string replaceStr, string numReplace)
        {
            if (numReplace == null)
            {
                numReplace = replaceStr;
            }
            if (replaceStr == null)
            {
                replaceStr = numReplace;
            }

            List<char> charList = new List<char>(charArry);
            int offset = 0;
            for (int i = 0, Imax = nodes.Count; i < Imax; i++)
            {
                int start = nodes[i].start + offset;
                int len = nodes[i].len;
                int endIndex = start + len - 1;
                string str = nodes[i].type == 0 ? replaceStr : numReplace;

                if (str.Length < len)
                {
                    charList.RemoveRange(start, len - str.Length);
                }
                for (int j = 0, Jmax = str.Length; j < Jmax; j++)
                {
                    char ch = str[j];
                    int index = start + j;
                    if (index <= endIndex)
                    {
                        charList[index] = ch;
                    }
                    else
                    {
                        charList.Insert(index, ch);
                    }
                }
                offset += str.Length - len;
            }
            return new string(charList.ToArray());
        }

        private int Check(string sourceText, int cursor)
        {
            int endsor = wordNodes.checkAndGetEndIndex(sourceText, cursor, CheckSpecial.CheckSpecialSym);
            int wordLength = endsor >= cursor ? endsor - cursor + 1 : 0;
            return wordLength;
        }

        private int CheckNumberSeq(string sourceText, int cursor, int filterNum)
        {
            int count = 0;
            int offset = 0;
            if (cursor + 1 >= sourceText.Length)
            {
                return 0;
            }
            //检测下位字符如果不是汉字 数字 字符 偏移量加1  
            for (int i = cursor + 1; i < sourceText.Length; i++)
            {
                //if(checkSpecialSym(sourceText[i]))
                //{
                //    offset++;
                //}
                //else 
                if (!CheckSpecial.IsNum(sourceText[i]))
                {
                    break;
                }
                else
                {
                    count++;
                }
            }
            if (count + 1 >= filterNum)
            {
                int wordLength = count + offset;
                return wordLength;
            }
            else
            {
                return 0;
            }
        }

        #region helper
        /// <summary>
        /// 转半角小写的函数(DBC case)
        /// </summary>
        /// <param name="input">任意字符串</param>
        /// <returns>半角字符串</returns>
        ///<remarks>
        ///全角空格为12288，半角空格为32
        ///其他字符半角(33-126)与全角(65281-65374)的对应关系是：均相差65248
        ///</remarks>
        private string ToDBC(string input)
        {
            char[] c = input.ToCharArray();
            for (int i = 0; i < c.Length; i++)
            {
                if (c[i] == 12288)
                {
                    c[i] = (char)32;
                    continue;
                }
                if (c[i] > 65280 && c[i] < 65375)
                    c[i] = (char)(c[i] - 65248);
            }
            return new string(c).ToLower();
        }
        #endregion
    }
}
